package top.kaoshanji.p2ch14a;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 代码清单-p2ch14a-405页a：BasicQueue的实现
 * author kaoshanji
 * ^_^
 * create time 2025/2/22 17:40
 */
public class BasicQueue {

    // 队列最多消息个数
    private static final int MAX_MSG_NUM = 1024 * 1024;

    // 消息体最大长度
    private static final int MAX_MSG_BODY_SIZE = 1020;

    // 每条消息占用的空间
    private static final int MSG_SIZE = MAX_MSG_BODY_SIZE + 4;

    // 队列消息体数据文件大小
    private static final int DATA_FILE_SIZE = MAX_MSG_NUM * MSG_SIZE;

    // 队列元数据文件大小（head + tail）
    private static final int META_SIZE = 8;

    private MappedByteBuffer dataBuf;
    private MappedByteBuffer metaBuf;

    public BasicQueue(String path, String queueName) throws IOException {
        if (!path.endsWith(File.separator)) {
            path += File.separator;
        }

        RandomAccessFile dataFile = null;
        RandomAccessFile metaFile = null;

        try {
            dataFile = new RandomAccessFile(path + queueName + ".data", "rw");
            metaFile = new RandomAccessFile(path + queueName + ".meat", "rw");

            // 一个巨大的字节数组，计算每条消息的索引位置
            dataBuf = dataFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, DATA_FILE_SIZE);

            // 只有 8 个字节数组，存储两个数字
            metaBuf = metaFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, META_SIZE);
        } finally {
            if (dataFile != null) {
                dataFile.close();
            }
            if (metaFile != null) {
                metaFile.close();
            }
        }
    }

    //---------------公共方法：队列数据--------------------------------

    // 入队
    public void enqueue(byte[] data) {
        if (data.length > MAX_MSG_BODY_SIZE) {
            throw new IllegalArgumentException("msg size is " + data.length
                    + ", while maximum allowed length is " + MAX_MSG_BODY_SIZE);
        }
        if (isFull()) {
            throw new IllegalStateException("queue is full");
        }

        int tail = tail(); // 当前队尾字节索引

        dataBuf.position(tail);

        dataBuf.putInt(data.length);
        dataBuf.put(data);

        if (tail + MSG_SIZE >= DATA_FILE_SIZE) {
            tail(0);
        } else {
            tail(tail + MSG_SIZE); // 本条消息存放的字节索引
        }

    }

    // 出队
    public byte[] dequeue() {
        if (isEmpty()) {
            return null;
        }

        int head = head();

        dataBuf.position(head);

        int length = dataBuf.getInt();
        byte[] data = new byte[length];
        dataBuf.get(data);

        if (head + MSG_SIZE >= DATA_FILE_SIZE) {
            head(0);
        } else {
            head(head + MSG_SIZE);
        }

        return data;
    }


    //------------------私有方法，元数据----------------------------

    // 获取队头的值
    private int head() {
        return metaBuf.getInt(0);
    }

    // 将队头设置指定值
    private void head(int newHead) {
        metaBuf.putInt(0, newHead);
    }

    // 获取队尾的值
    private int tail() {
        return metaBuf.getInt(4);
    }

    // 设置队尾的值
    private void tail(int newTail) {
        metaBuf.putInt(4, newTail);
    }

    // 判断队列是否是空的
    private boolean isEmpty() {
        return head() == tail();
    }

    // 判断队列是否已经满了
    private boolean isFull() {
        return ((tail() + MSG_SIZE) % DATA_FILE_SIZE) == head();
    }

}
